/***********************************************
这段代码展示了一个简单的字符设备驱动示例，这是最简单的代码，
没有实质意义的操作，但是对于理解驱动开发的框架流程很重要

尽可能的对代码作出注释是十分必要的～～～
代码如何审阅：
1，基本结构与宏定义
2，init与exit结构————file_operations结构
3，具体的操作定义
************************************************/

/*********************************
字符设备分析：
1、struct globalmem_dev——设备结构
2、struct file_operations globalmem_fops————驱动操作
3、int globalmem_init(void)——————初始化加载
	一、分配设备号
	二、实体化globalmem_dev(下面有一个struct cdev)
	三、将fops挂到cdev下面(设备与驱动的匹配)——一般开发不会这么写，这里只是理解罢了
驱动操作：
	read、write操作，用户/内核 信息的转换
*********************************/

#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <linux/fs.h>

#include <linux/slab.h>
/*************************
定义全局数据
**************************/
#define GLOBALMEM_SIZE 0x100 	//全局内存大小	4KB
#define MEM_CLEAR 0X1  			//清零全局内存
#define GLOBALMEM_MAJOR 250		//预设的主设备号

static int globalmem_major = GLOBALMEM_MAJOR;	//主设备号

//设备结构定义
struct globalmem_dev{
	struct cdev cdev;				//字符设备描述结构定义
	unsigned char mem[GLOBALMEM_SIZE];	//全局内存
};
struct globalmem_dev *globalmem_devp;//设备结构指针


//open操作
int globalmem_open(struct inode *inode,struct file *filp){
	//将设备结构指针赋值给文件结构指针
	filp->private_data = globalmem_devp;
	return 0;
}
//文件释放函数
int globalmem_release(struct inode *inode,struct file *filp){
	
	return 0;
}

//read操作
static ssize_t globalmem_read(struct file *filp,
		char __user *buf,size_t size,loff_t *ppos){
		
	unsigned int count = size;
	unsigned long p = *ppos;
	int ret = 0;
	struct globalmem_dev *dev = filp->private_data;
	
	//判断读的偏移位置是否越界
	if(p >= GLOBALMEM_SIZE){
		return 0;
	}
	//判断要读的字节数
	if(count > GLOBALMEM_SIZE - p){
		count = GLOBALMEM_SIZE - p;
	}
	//内核空间到用户空间
	if(copy_to_user(buf,(void*)(dev->mem + p),count)){
		ret = -EFAULT;
	}else{
		*ppos += count;
		ret = count;
		
		printk(KERN_INFO "read %u bytes from %lu\n",count,p);
	}
	return ret;
}
//write操作
static ssize_t globalmem_write(struct file *filp,
		char __user *buf,size_t size,loff_t *ppos){
		
	unsigned long p = *ppos;
	unsigned int count = size;
	int ret = 0;
	struct globalmem_dev *dev = filp->private_data;
	
	//判断写的偏移位置是否越界
	if(p >= GLOBALMEM_SIZE){
		return 0;
	}
	//判断要写的字节数
	if(count > GLOBALMEM_SIZE - p){
		count = GLOBALMEM_SIZE - p;
	}
	//用户空间到内核空间
	if(copy_from_user(dev->mem + p,buf,count)){
		ret = -EFAULT;
	}else{
		*ppos += count;
		ret = count;
		
		printk(KERN_INFO "written %u bytes from %lu\n",count,p);
	}
	return ret;
}
//seek操作
static loff_t globalmem_seek(struct file *filp,
		loff_t offset,int orig){
	
	loff_t ret = 0;
	
	switch(orig)
	{
		//文件头开始偏移
		case 0:
			//偏移位置0
			if(offset < 0){
				ret = -EINVAL;
				break;
			}
			//偏移越界
			if((unsigned int)offset > GLOBALMEM_SIZE){
				ret = -EINVAL;
				break;
			}
			filp->f_pos = (unsigned int)offset;
			ret = filp->f_pos;
			break;
		//从当前位置开始偏移
		case 1:
			//偏移越界
			if((filp->f_pos + offset) > GLOBALMEM_SIZE){
				ret = -EINVAL;
				break;
			}
			//偏移位置小于0
			if((filp->f_pos + offset) < 0){
				ret = -EINVAL;
				break;
			}
			filp->f_pos +=offset;
			ret = filp->f_pos;
			break;
		default:
			ret = -EINVAL;
	}
	return ret;
}

//设备驱动操作结构
static const struct file_operations globalmem_fops={
	.owner = THIS_MODULE,
	.llseek = globalmem_seek,
	.read = globalmem_read,
	.write = globalmem_write,
	.open = globalmem_open,
	.release = globalmem_release,
};
//初始化cdev结构
static void globalmem_setup_cdev(struct globalmem_dev *dev,int index){
	int err;
	int devno =MKDEV(globalmem_major,0);
	
	cdev_init(&dev->cdev,&globalmem_fops);
	dev->cdev.owner = THIS_MODULE;
	
	err=cdev_add(&dev->cdev,devno,1);
	if(err){
		printk(KERN_NOTICE "error %d adding globalmem %d",err,index);
	}
}

//加载函数
int globalmem_init(void){
	int ret;
	
	//申请设备号
	dev_t devno = MKDEV(globalmem_major,0);
	if(globalmem_major)
		ret = register_chrdev_region(devno,1,"globalmem");
	else{
		ret = alloc_chrdev_region(&devno,0,1,"globalmem");
		globalmem_major = MAJOR(devno);
	}
	if(ret < 0)
		return ret;
	//动态申请设备结构内存
	
	globalmem_devp = kmalloc(sizeof(struct globalmem_dev),GFP_KERNEL);
	if(!globalmem_devp){
		ret = -ENOMEM;
		goto fail_malloc;
	}
	//初始化cdev结构
	globalmem_setup_cdev(globalmem_devp,0);
	return 0;
	
	fail_malloc:
		unregister_chrdev_region(devno,1);
	return ret;
}

//卸载函数
int globalmem_exit(void){
	cdev_del(&globalmem_devp->cdev);	//注销cdev
	kfree(globalmem_devp);	//释放设备结构内存
	unregister_chrdev_region(MKDEV(globalmem_major,0),1);//释放设备号
}

MODULE_AUTHOR("from linux device driver design");
MODULE_LICENSE("Dual BSD/GPL");

module_init(globalmem_init);
module_exit(globalmem_exit);




